home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / contributed / waste / waste 1.3 / source / wemouse.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  38.3 KB  |  1,646 lines

  1. /*
  2.  *    WEMouse.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Mouse Clicks and Support for Drag and Drop
  6.  *
  7.  *  Copyright (c) 1993-1998 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __FOLDERS__
  18. #include <Folders.h>
  19. #endif
  20.  
  21. #if WASTE_IC_SUPPORT
  22. #ifndef __ICTYPES__
  23. #include "ICTypes.h"
  24. #endif
  25. #ifndef __ICAPI__
  26. #include "ICAPI.h"
  27. #endif
  28. #ifndef __PROCESSES__
  29. #include <Processes.h>
  30. #endif
  31. #endif // WASTE_IC_SUPPORT
  32.  
  33. #if WASTE_DRAG_AND_DROP
  34.  
  35. //    static variables
  36.  
  37. static DragSendDataUPP _weFlavorSender = nil ;
  38.  
  39. pascal Boolean _WEIsOptionDrag ( DragReference drag )
  40. {
  41.     EventModifiers downModifiers, upModifiers ;
  42.  
  43.     // get drag modifiers
  44.     // ??? shouldn't GetDragModifiers take EventModifiers* parameters ???
  45.     GetDragModifiers ( drag, nil, ( SInt16 * ) & downModifiers, ( SInt16 * ) & upModifiers ) ;
  46.  
  47.     // return true if the option key was held down at the beginning and/or at the end
  48.     return ( ( ( downModifiers | upModifiers ) & optionKey ) != 0 ) ;
  49. }
  50.  
  51. pascal OSErr _WEGetFlavor ( DragReference drag, ItemReference dragItem,
  52.                 FlavorType requestedType, Handle hFlavor,
  53.                 SInt32 dropOffset, WEHandle hWE )
  54. {
  55.     // get the requested flavor out of the specified drag reference and put it into
  56.     // the given handle, if any -- if hFlavor is nil, just check whether the specified flavor
  57.     // is there or can be obtained by invoking a user-defined translation routine
  58.  
  59.     FlavorFlags flags ;
  60.     Size dataSize ;
  61.     WETranslateDragUPP translateDragHook ;
  62.     Boolean saveFlavorLock ;
  63.     OSErr err ;
  64.  
  65.     // see if the drag item has the requested flavor type,
  66.     // without forcing the actual data to be sent and/or translated
  67.     err = GetFlavorFlags ( drag, dragItem, requestedType, & flags ) ;
  68.     if ( err == badDragFlavorErr )
  69.     {
  70.         // requested flavor is not available: our client may try a custom translation
  71.         // this is especially handy to translate HFS objects like TEXT and PICT files
  72.         if ( ( translateDragHook = ( * hWE ) -> translateDragHook ) != nil )
  73.         {
  74.             err = CallWETranslateDragProc ( drag, dragItem, requestedType, hFlavor,
  75.                         dropOffset, hWE, translateDragHook ) ;
  76.         }
  77.     }
  78.     else if ( err == noErr )
  79.     {
  80.  
  81.         // requested flavor is available: get it if hFlavor is not nil
  82.         if ( hFlavor == nil )
  83.         {
  84.             return err ;
  85.         }
  86.  
  87.         // get size of flavor data
  88.         if ( ( err = GetFlavorDataSize( drag, dragItem, requestedType, & dataSize ) ) != noErr )
  89.         {
  90.             return err ;
  91.         }
  92.  
  93.         // resize the handle
  94.         SetHandleSize ( hFlavor, dataSize ) ;
  95.         if ( ( err = MemError ( ) ) != noErr )
  96.         {
  97.             return err ;
  98.         }
  99.  
  100.         // get flavor data
  101.         saveFlavorLock = _WESetHandleLock ( hFlavor, true ) ;
  102.         err = GetFlavorData ( drag, dragItem, requestedType, * hFlavor, & dataSize, 0 ) ;
  103.         _WESetHandleLock ( hFlavor, saveFlavorLock ) ;
  104.     }
  105.  
  106.     return err;
  107. }
  108.  
  109. pascal OSErr _WEExtractFlavor ( DragReference drag, ItemReference dragItem,
  110.                         FlavorType theType, Handle *hFlavor,
  111.                         SInt32 dropOffset, WEHandle hWE )
  112. {
  113.     OSErr err;
  114.  
  115.     // allocate a new handle
  116.     if ( ( err = _WEAllocate ( 0, kAllocTemp, hFlavor ) ) == noErr )
  117.     {
  118.         // put the requested flavor into this handle
  119.         if ( ( err = _WEGetFlavor ( drag, dragItem, theType, * hFlavor, dropOffset, hWE ) ) != noErr )
  120.         {
  121.  
  122.         // if an error occurred, forget the handle
  123.             _WEForgetHandle ( hFlavor ) ;
  124.         }
  125.     }
  126.  
  127.     return err ;
  128. }
  129.  
  130. pascal void _WEUpdateDragCaret ( SInt32 offset, WEHandle hWE )
  131. {
  132.     WEPtr pWE = * hWE ;        // assume WE record is already locked
  133.     UInt32 currentTime ;
  134.  
  135.     // get current time
  136.     currentTime = TickCount ( ) ;
  137.  
  138.     if ( offset == pWE -> dragCaretOffset )
  139.     {
  140.  
  141.         // drag caret offset didn't change; blink the caret
  142.         if ( ( currentTime > pWE -> caretTime + GetCaretTime ( ) ) && ( offset != kInvalidOffset ) )
  143.         {
  144.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  145.             BCHG ( pWE -> flags, weFDragCaretVisible ) ;        // invert flag
  146.             pWE -> caretTime = currentTime ;
  147.         }
  148.     }
  149.     else
  150.     {
  151.  
  152.         // drag caret offset did change
  153.         // hide old caret, if it's showing
  154.         if ( BTST ( pWE -> flags, weFDragCaretVisible ) )
  155.         {
  156.             _WEDrawCaret ( pWE -> dragCaretOffset, hilite, false, hWE ) ;
  157.         }
  158.  
  159.         // show new caret (unless offset is kInvalidOffset)
  160.         if ( offset != kInvalidOffset )
  161.         {
  162.             _WEDrawCaret ( offset, hilite, false, hWE ) ;
  163.             BSET ( pWE -> flags, weFDragCaretVisible ) ;
  164.             pWE -> caretTime = currentTime ;
  165.         }
  166.         else
  167.         {
  168.             BCLR ( pWE -> flags, weFDragCaretVisible ) ;
  169.         }
  170.  
  171.         // remember drag caret offset
  172.         pWE -> dragCaretOffset = offset ;
  173.     }
  174. }
  175.  
  176. static pascal OSErr _WESendFlavor(FlavorType requestedType, void *dragSendRefCon, WEHandle hWE, DragReference drag)
  177. {
  178. #pragma unused(dragSendRefCon)
  179.  
  180. #if !GENERATINGCFM
  181.     SInt32 saveA5 = SetCurrentA5();    // this fixes a conflict with HoverBar
  182.                                     // (well, probably a bug in the Drag Manager)
  183. #endif
  184.  
  185.     WEPtr pWE = *hWE;
  186.     SInt32 selStart = pWE->selStart;
  187.     SInt32 selEnd = pWE->selEnd;
  188.     Handle hItem = nil;
  189.     Boolean disposeItem = true;        // dispose of item when done
  190. #if WASTE_OBJECTS
  191.     WEObjectDescHandle hObjectDesc;
  192. #endif
  193.     OSErr err;
  194.  
  195.     // allocate a temporary handle to hold a copy of the requested flavor
  196.     if ((err = _WEAllocate(0, kAllocTemp, &hItem)) != noErr)
  197.     {
  198.         goto cleanup;
  199.     }
  200.  
  201. #if WASTE_OBJECTS
  202.     // see if the selection contains an embedded object whose type matches the flavortype
  203.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  204.     {
  205.         FlavorType theType;
  206.  
  207.         if ((err = _WEStreamObject(weToDrag, &theType, &hItem, &disposeItem, hObjectDesc)) != noErr)
  208.         {
  209.             goto cleanup;
  210.         }
  211.  
  212.         // make sure theType matches the requested type
  213.         err = badDragFlavorErr;
  214.         if (theType != requestedType)
  215.         {
  216.             goto cleanup;
  217.         }
  218.     }
  219.     else
  220. #endif
  221.     {
  222.         // identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP'
  223.         if (requestedType == kTypeText)
  224.         {
  225.             err = WECopyRange(selStart, selEnd, hItem, nil, nil, hWE);
  226.         }
  227.         else if (requestedType == kTypeStyles)
  228.         {
  229.             err = WECopyRange(selStart, selEnd, nil, hItem, nil, hWE);
  230.         }
  231.         else if (requestedType == kTypeFontTable)
  232.         {
  233.             err = WEBuildFontTable(hItem, nil, hWE);
  234.         }
  235. #if WASTE_OBJECTS
  236.         else if (requestedType == kTypeSoup)
  237.         {
  238.             err = WECopyRange(selStart, selEnd, nil, nil, hItem,hWE);
  239.         }
  240. #endif
  241.         else
  242.         {
  243.             err = badDragFlavorErr;
  244.         }
  245.  
  246.         if (err != noErr)
  247.         {
  248.             goto cleanup;
  249.         }
  250.     }
  251.  
  252.     // set the drag flavor data
  253.     HLock(hItem);
  254.     err = SetDragItemFlavorData(drag, (ItemReference) hWE, requestedType, *hItem,
  255.             GetHandleSize(hItem), 0);
  256.     HUnlock(hItem);
  257.  
  258. cleanup:
  259.     if (disposeItem)
  260.     {
  261.         _WEForgetHandle(&hItem);
  262.     }
  263.  
  264. #if !GENERATINGCFM
  265.     SetA5(saveA5);
  266. #endif
  267.  
  268.     // return result code
  269.     return err;
  270.  
  271. }
  272.  
  273. pascal RgnHandle _WEOutlineRgn ( RgnHandle solidRgn )
  274. {
  275.     RgnHandle outlineRgn ;
  276.  
  277.     //    make an outline region out of the given solid region
  278.     if ( ( outlineRgn = NewRgn ( ) ) != nil )
  279.     {
  280.         CopyRgn ( solidRgn, outlineRgn ) ;
  281.         InsetRgn ( outlineRgn, 1, 1 ) ;
  282.         DiffRgn ( solidRgn, outlineRgn, outlineRgn ) ;
  283.     }
  284.  
  285.     return outlineRgn ;
  286. }
  287.  
  288. #if WASTE_TRANSLUCENT_DRAGS
  289.  
  290. pascal OSErr _WEMakeDragImage ( GWorldPtr * imageGWorld, RgnHandle * imageRgn, WEHandle hWE )
  291. {
  292.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  293.     PixMapHandle pixels = nil ;
  294.     Point offset ;
  295.     Rect localBounds ;
  296.     Rect globalBounds ;
  297.     SInt32 dragArea ;
  298.     GDHandle saveDevice ;
  299.     CGrafPtr savePort ;
  300.     GrafPtr textPort ;
  301.     Boolean wasInactive = false ;
  302.     OSErr err ;
  303.  
  304.     //    init return values
  305.     * imageGWorld = nil ;
  306.     * imageRgn = nil ;
  307.  
  308.     //    save current graphics world
  309.     GetGWorld ( & savePort, & saveDevice ) ;
  310.  
  311.     //    set up the port
  312.     textPort = pWE -> port ;
  313.     SetPort ( textPort ) ;
  314.  
  315.     //    calculate delta between global coords and local coords
  316.     offset . v = 0 ;
  317.     offset . h = 0 ;
  318.     LocalToGlobal ( & offset ) ;
  319.  
  320.     //    get image region (in local coordinates)
  321.     if ( ( * imageRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  322.     {
  323.         err = memFullErr ;
  324.         goto cleanup ;
  325.     }
  326.  
  327.     //    get bounding rectangle of image region
  328.     localBounds = ( ** imageRgn ) -> rgnBBox ;
  329.  
  330.     //    translate to global coordinates
  331.     globalBounds = localBounds ;
  332.     OffsetRect ( & globalBounds, offset . h, offset . v ) ;
  333.  
  334.     //    calculate drag region area
  335.     dragArea = ( ( SInt32 ) globalBounds . right - globalBounds . left ) *
  336.                ( ( SInt32 ) globalBounds . bottom - globalBounds . top ) ;
  337.  
  338.     //    if translucent drags are available and drag region area
  339.     //    is reasonably small, prepare a drag image
  340.     if ( ( dragArea <= pWE -> translucencyThreshold ) && BTST ( pWE -> flags, weFHasTranslucentDrags ) )
  341.     {
  342.  
  343.         //    create an 8-bit deep gworld for drawing the drag image
  344.         if ( ( err = NewGWorld ( imageGWorld, 8, & globalBounds, nil, nil, useTempMem ) ) == noErr )
  345.         {
  346.             //    get the pixmap from the gworld
  347.             pixels = GetGWorldPixMap ( * imageGWorld ) ;
  348.  
  349.             //    set up the gworld
  350.             SetGWorld ( * imageGWorld, nil ) ;
  351.             LockPixels ( pixels ) ;
  352.             SetOrigin ( localBounds . left, localBounds . top ) ;
  353.  
  354.             //    temporarily set weFActive so we'll draw the full highlighting
  355.             if ( ! BTST ( pWE -> flags, weFActive ) )
  356.             {
  357.                 wasInactive = true ;
  358.                 BSET ( pWE -> flags, weFActive ) ;
  359.             }
  360.  
  361.             //    draw the selection into the gworld
  362.             EraseRect ( & localBounds ) ;
  363.             pWE -> port = ( GrafPtr ) * imageGWorld ;
  364.             WEUpdate ( * imageRgn, hWE ) ;
  365.             pWE -> port = textPort ;
  366.  
  367.             //    restore original setting of weFActive
  368.             if ( wasInactive )
  369.             {
  370.                 BCLR ( pWE -> flags, weFActive ) ;
  371.             }
  372.  
  373.             //    end drawing
  374.             UnlockPixels ( pixels ) ;
  375.             SetOrigin ( 0, 0 ) ;
  376.         }
  377.     }
  378.  
  379.     //    convert the image region to global coordinates
  380.     OffsetRgn ( * imageRgn, offset . h, offset . v ) ;
  381.  
  382.     if ( pixels != nil )
  383.     {
  384.         //    convert the image itself to global coordinates
  385.         offset = topLeft ( ( ** imageRgn ) -> rgnBBox ) ;
  386.         OffsetRect ( & ( * pixels ) -> bounds, offset . h, offset . v ) ;
  387.     }
  388.  
  389.     //    clear result code
  390.     err = noErr ;
  391.  
  392. cleanup :
  393.     //    restore original graphics world
  394.     SetGWorld ( savePort, saveDevice ) ;
  395.  
  396.     //    return result code
  397.     return err ;
  398. }
  399.  
  400. #endif    // WASTE_TRANSLUCENT_DRAGS
  401.  
  402. pascal OSErr _WEDrag ( Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE )
  403. {
  404.     WEPtr pWE = * hWE ;        //    assume WE record is already locked
  405.     DragReference drag = kNullDrag ;
  406.     RgnHandle dragRgn = nil ;
  407.     RgnHandle dragOutline = nil ;
  408.     EventRecord event ;
  409.     Rect dragBounds ;
  410.     Point tempPoint ;
  411.     GrafPtr savePort ;
  412.     OSErr err ;
  413. #if WASTE_OBJECTS
  414.     WEObjectDescHandle hObjectDesc ;
  415. #endif
  416. #if WASTE_TRANSLUCENT_DRAGS
  417.     GWorldPtr dragGWorld = nil ;
  418. #endif
  419.  
  420.     // set up the port
  421.     GetPort ( & savePort ) ;
  422.     SetPort ( pWE -> port ) ;
  423.  
  424.     // turn the cursor into an arrow
  425.     SetCursor ( & qd . arrow ) ;
  426.  
  427.     // fabricate an EventRecord for TrackDrag
  428.     event . what = mouseDown ;
  429.     event . message = 0 ;
  430.     event . when = clickTime ;
  431.     event . where = mouseLoc ;
  432.     LocalToGlobal ( & event . where ) ;
  433.     event . modifiers = modifiers ;
  434.  
  435.     // to start the drag, the user must move the mouse a certain distance
  436.     // away from the initial mouse location; this allows a short click in
  437.     // the selection area to set the insertion point
  438.     err = weNoDragErr ;
  439.     if ( ! WaitMouseMoved ( event . where ) )
  440.     {
  441.         goto cleanup ;
  442.     }
  443.  
  444.     // create a drag object
  445.     if ( ( err = NewDrag ( & drag ) ) != noErr )
  446.     {
  447.         goto cleanup ;
  448.     }
  449.  
  450. #if WASTE_OBJECTS
  451.     // if the selection range consists of an embedded object,
  452.     // then use its object type as flavor type
  453.     if ( WEGetSelectedObject ( & hObjectDesc, hWE ) == noErr )
  454.     {
  455.         FlavorType theType ;
  456.         Handle theData = nil ;
  457.         Boolean disposeData ;
  458.  
  459.         if ( ( err = _WEStreamObject ( weToDrag, & theType, & theData, & disposeData, hObjectDesc ) ) != noErr )
  460.         {
  461.             goto cleanup ;
  462.         }
  463.  
  464.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, theType, nil, 0, 0 ) ) != noErr )
  465.         {
  466.             goto cleanup ;
  467.         }
  468.     }
  469.     else
  470. #endif
  471.     {
  472.  
  473.         // add a 'TEXT' flavor to the drag
  474.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeText, nil, 0, 0 ) ) != noErr )
  475.         {
  476.             goto cleanup ;
  477.         }
  478.  
  479.         // add a 'styl' flavor to the drag
  480.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeStyles, nil, 0, 0 ) ) != noErr )
  481.         {
  482.             goto cleanup ;
  483.         }
  484.  
  485.         // add a 'FISH' flavor to the drag
  486.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeFontTable, nil, 0, 0 ) ) != noErr )
  487.         {
  488.             goto cleanup ;
  489.         }
  490.  
  491. #if WASTE_OBJECTS
  492.         // add a 'SOUP' flavor to the drag
  493.         if ( ( err = AddDragItemFlavor ( drag, ( ItemReference ) hWE, kTypeSoup, nil, 0, 0 ) ) != noErr )
  494.         {
  495.             goto cleanup ;
  496.         }
  497. #endif
  498.  
  499.     }
  500.  
  501.     // since we didn't provide the flavor data for any of the above flavors,
  502.     // we need supply a data send callback
  503.     if ( _weFlavorSender == nil )
  504.     {
  505.         _weFlavorSender = NewDragSendDataProc ( _WESendFlavor ) ;
  506.     }
  507.  
  508.     if ( ( err = SetDragSendProc ( drag, _weFlavorSender, 0 ) ) != noErr )
  509.     {
  510.         goto cleanup ;
  511.     }
  512.  
  513. #if WASTE_TRANSLUCENT_DRAGS
  514.     //    get drag image & region
  515.     if ( ( err = _WEMakeDragImage( & dragGWorld, & dragRgn, hWE ) ) != noErr )
  516.     {
  517.         goto cleanup ;
  518.     }
  519. #else
  520.     //    get drag region (in local coordinates)
  521.     if ( ( dragRgn = WEGetHiliteRgn ( pWE -> selStart, pWE -> selEnd, hWE ) ) == nil )
  522.     {
  523.         err = memFullErr ;
  524.         goto cleanup ;
  525.     }
  526.  
  527.     //    convert drag region to global coordinates
  528.     tempPoint . v = 0 ;
  529.     tempPoint . h = 0 ;
  530.     LocalToGlobal ( & tempPoint ) ;
  531.     OffsetRgn ( dragRgn, tempPoint . h, tempPoint . v ) ;
  532. #endif
  533.  
  534.     //    set the bounds of the drag
  535.     dragBounds = ( * dragRgn ) -> rgnBBox ;
  536.     if ( ( err = SetDragItemBounds ( drag, ( ItemReference ) hWE, & dragBounds ) ) != noErr )
  537.     {
  538.         goto cleanup ;
  539.     }
  540.  
  541. #if WASTE_TRANSLUCENT_DRAGS
  542.     //    set drag image, if any
  543.     if ( dragGWorld != nil )
  544.     {
  545.         tempPoint . v = 0 ;
  546.         tempPoint . h = 0 ;
  547.         SetDragImage ( drag, GetGWorldPixMap ( dragGWorld ), dragRgn, tempPoint, kDragStandardTranslucency ) ;
  548.     }
  549. #endif
  550.  
  551.     // create an outline region for TrackDrag
  552.     dragOutline = _WEOutlineRgn ( dragRgn ) ;
  553.  
  554.     //    call the pre-TrackDrag hook, if any
  555.     if ( pWE -> preTrackDragHook != nil )
  556.     {
  557.         if ( ( err = CallWEPreTrackDragProc ( drag, hWE, pWE -> preTrackDragHook ) ) != noErr )
  558.         {
  559.             goto cleanup ;
  560.         }
  561.     }
  562.  
  563.     // stash drag reference in currentDrag so WETrackDrag and WEReceiveDrag
  564.     // can tell whether a given drag originated from this WE instance
  565.     pWE -> currentDrag = drag ;
  566.  
  567.     // track the drag
  568.     err = TrackDrag ( drag, & event, dragOutline ) ;
  569.     pWE -> currentDrag = kNullDrag ;
  570.     if (err != noErr)
  571.     {
  572.         goto cleanup ;
  573.     }
  574.  
  575.     // if the selection was dragged to the trash and the option key wasn't held down
  576.     // and if the instance is editable, delete the selection
  577.     if ( ! BTST ( pWE -> features, weFReadOnly ) )
  578.     {
  579.         if ( WEDraggedToTrash ( drag ) )
  580.         {
  581.             if ( ! _WEIsOptionDrag ( drag ) )
  582.             {
  583.                 if ( ( err = WEDelete ( hWE ) ) != noErr )
  584.                 {
  585.                     goto cleanup ;
  586.                 }
  587.             }
  588.         }
  589.     }
  590.  
  591.     // clear result code
  592.     err = noErr ;
  593.  
  594. cleanup:
  595.     // dispose of the drag
  596.     if ( drag != kNullDrag )
  597.     {
  598.         DisposeDrag ( drag ) ;
  599.     }
  600.  
  601. #if WASTE_TRANSLUCENT_DRAGS
  602.     //    dispose of the image gworld
  603.     if ( dragGWorld != nil )
  604.     {
  605.         DisposeGWorld ( dragGWorld ) ;
  606.     }
  607. #endif
  608.  
  609.     //    dispose of auxiliary regions
  610.     if ( dragRgn != nil )
  611.     {
  612.         DisposeRgn ( dragRgn ) ;
  613.     }
  614.     if ( dragOutline != nil )
  615.     {
  616.         DisposeRgn ( dragOutline ) ;
  617.     }
  618.  
  619.     // restore the port
  620.     SetPort ( savePort ) ;
  621.  
  622.     // return result code
  623.     return err;
  624. }
  625.  
  626. #endif    // WASTE_DRAG_AND_DROP
  627.  
  628. pascal Boolean WECanAcceptDrag ( DragReference drag, WEHandle hWE )
  629. {
  630. #if WASTE_DRAG_AND_DROP
  631.     WEPtr pWE ;
  632.     UInt16 numDragItems ;
  633.     UInt16 dragItemIndex ;
  634.     ItemReference dragItem ;
  635.     Boolean saveWELock ;
  636.     OSErr err ;
  637.     Boolean retval = false ;
  638.  
  639.     // lock the WE record
  640.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  641.     pWE = * hWE ;
  642.  
  643.     // refuse all drags if the weFReadOnly feature is enabled
  644.     if ( BTST ( pWE -> features, weFReadOnly ) )
  645.     {
  646.         goto cleanup ;
  647.     }
  648.  
  649.     // count items in this drag
  650.     if ( ( err = CountDragItems ( drag, & numDragItems ) ) != noErr )
  651.     {
  652.         goto cleanup ;
  653.     }
  654.  
  655.     for ( dragItemIndex = 1 ; dragItemIndex <= numDragItems ; dragItemIndex ++ )
  656.     {
  657.         // get item reference number for current drag item
  658.         if ( ( err = GetDragItemReferenceNumber ( drag, dragItemIndex, & dragItem ) ) != noErr )
  659.         {
  660.             goto cleanup ;
  661.         }
  662.  
  663.         // see if this drag item contains a text flavor
  664.         err = _WEGetFlavor ( drag, dragItem, kTypeText, nil, kInvalidOffset, hWE ) ;
  665.  
  666. #if WASTE_OBJECTS
  667.         if ( err == badDragFlavorErr )
  668.         {
  669.             SInt32 objectIndex ;
  670.             FlavorType objectType ;
  671.  
  672.             // see if this drag item contains a flavor matching one of the registered object types
  673.             for ( objectIndex = 0 ;
  674.                   _WEGetIndObjectType ( objectIndex, & objectType, hWE ) == noErr ;
  675.                   objectIndex ++ )
  676.             {
  677.                 if ( ( err = _WEGetFlavor(drag, dragItem, objectType, nil, kInvalidOffset, hWE ) ) != badDragFlavorErr )
  678.                 {
  679.                     break ;
  680.                 }
  681.             }
  682.         }
  683. #endif    // WASTE_OBJECTS
  684.  
  685.         if ( err != noErr )
  686.         {
  687.             goto cleanup ;
  688.         }
  689.     } // for
  690.  
  691.     retval = true;
  692.  
  693. cleanup:
  694.  
  695.     // unlock the WE record
  696.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  697.  
  698.     return retval ;
  699. #else
  700.     #pragma unused(drag, hWE)
  701.     return false ;
  702. #endif    // WASTE_DRAG_AND_DROP
  703. }
  704.  
  705. pascal OSErr WETrackDrag ( DragTrackingMessage message, DragReference drag, WEHandle hWE )
  706. {
  707. #if WASTE_DRAG_AND_DROP
  708.     WEPtr pWE ;
  709.     DragAttributes attributes ;
  710.     Point mouse ;
  711.     LongPt thePoint ;
  712.     SInt32 offset ;
  713.     WEEdge edge ;
  714.     Boolean saveWELock ;
  715.     OSErr err ;
  716.  
  717.     // lock the WE record
  718.     saveWELock = _WESetHandleLock ( ( Handle ) hWE, true ) ;
  719.     pWE = * hWE ;
  720.  
  721.     // dispatch on message
  722.     switch ( message )
  723.     {
  724.         case kDragTrackingEnterWindow :
  725.         {
  726.             // determine whether we can accept this drag
  727.             if ( WECanAcceptDrag ( drag, hWE ) )
  728.             {
  729.                 BSET ( pWE -> flags, weFCanAcceptDrag ) ;
  730.             }
  731.             else
  732.             {
  733.                 BCLR ( pWE -> flags, weFCanAcceptDrag ) ;
  734.             }
  735.  
  736.             // reset clickTime
  737.             pWE -> clickTime = 0 ;
  738.             break ;
  739.         }
  740.  
  741.         case kDragTrackingInWindow :
  742.         {
  743.             if ( BTST ( pWE -> flags, weFCanAcceptDrag ) )
  744.             {
  745.  
  746.                 // get drag attributes
  747.                 if ( ( err = GetDragAttributes ( drag, & attributes ) ) != noErr )
  748.                 {
  749.                     goto cleanup;
  750.                 }
  751.  
  752.                 // get current mouse location in local coordinates
  753.                 if ( ( err = GetDragMouse ( drag, & mouse, nil ) ) != noErr )
  754.                 {
  755.                     goto cleanup;
  756.                 }
  757.                 GlobalToLocal ( & mouse ) ;
  758.  
  759.                 if ( PtInRgn ( mouse, pWE -> viewRgn ) )
  760.                 {
  761.                     // mouse is in text area
  762.                     // hilite the text rectangle, if we haven't already
  763.                     // and if the drag has left sender window since drag tracking started
  764.                     if ( ( ! BTST ( pWE -> flags, weFHilited ) ) &&
  765.                          ( attributes & kDragHasLeftSenderWindow ) )
  766.                     {
  767.                         CallWEHiliteDropAreaProc ( drag, true, hWE, pWE -> hiliteDropAreaHook ) ;
  768.                         BSET ( pWE -> flags, weFHilited ) ;
  769.                     }
  770.  
  771.                     // hide the regular caret
  772.                     if ( BTST ( pWE -> flags, weFCaretVisible ) )
  773.                     {
  774.                         _WEBlinkCaret ( hWE ) ;
  775.                     }
  776.  
  777.                     // get text offset corresponding to mouse location
  778.                     WEPointToLongPoint ( mouse, & thePoint ) ;
  779.                     offset = WEGetOffset ( & thePoint, & edge, hWE ) ;
  780.  
  781.                     // if offset is within the original selection range, don't display drag feedback
  782.                     if ( drag == pWE -> currentDrag )
  783.                     {
  784.                         if ( _WEOffsetInRange ( offset, edge, pWE -> selStart, pWE -> selEnd ) )
  785.                         {
  786.                             offset = kInvalidOffset ;
  787.                         }
  788.                     }
  789.  
  790.                     // provide a drag feedback in the form of a blinking caret
  791.                     _WEUpdateDragCaret ( offset, hWE ) ;
  792.  
  793.                     // clear clickTime
  794.                     pWE -> clickTime = 0 ;
  795.                 }
  796.                 else
  797.                 {
  798.  
  799.                     // mouse is outside text area
  800.                     // dehilite the text rectangle, if it's hilited
  801.                     if ( BTST ( pWE -> flags, weFHilited ) )
  802.                     {
  803.                         CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  804.                         BCLR ( pWE -> flags, weFHilited ) ;
  805.                     }
  806.  
  807.                     // hide the drag caret, if it's showing
  808.                     _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  809.  
  810.                     // if the mouse has been remaining outside the view region for 10 ticks or more
  811.                     // and this drag was created by this WE instance, call the click loop routine
  812.                     if ( drag == pWE->currentDrag )
  813.                     {
  814.                         UInt32 currentTime = TickCount ( ) ;
  815.                         if ( pWE -> clickTime == 0 )
  816.                         {
  817.                             pWE -> clickTime = currentTime ;
  818.                         }
  819.                         else if ( currentTime > pWE -> clickTime + kAutoScrollDelay )
  820.                         {
  821.                             if ( pWE -> clickLoop != nil )
  822.                             {
  823.                                 CallWEClickLoopProc ( hWE, pWE -> clickLoop ) ;
  824.                             }
  825.                         }
  826.                     }
  827.                 }
  828.             }
  829.             break ;
  830.         }
  831.  
  832.         case kDragTrackingLeaveWindow :
  833.         {
  834.  
  835.             // drag has left this window
  836.             // dehilite the text area if necessary
  837.             if ( BTST ( pWE -> flags, weFHilited ) )
  838.             {
  839.                 CallWEHiliteDropAreaProc ( drag, false, hWE, pWE->hiliteDropAreaHook ) ;
  840.                 BCLR ( pWE -> flags, weFHilited ) ;
  841.             }
  842.  
  843.             // hide the drag caret, if it's showing
  844.             _WEUpdateDragCaret ( kInvalidOffset, hWE ) ;
  845.             break;
  846.         }
  847.  
  848.         default:
  849.         {
  850.             break;
  851.         }
  852.     }
  853.  
  854.     // clear result code
  855.     err = noErr ;
  856.  
  857. cleanup:
  858.     // unlock the WE record
  859.     _WESetHandleLock ( ( Handle ) hWE, saveWELock ) ;
  860.  
  861.     // return result code
  862.     return err ;
  863. #else
  864.     #pragma unused(message, drag, hWE)
  865.     return noErr ;
  866. #endif
  867. }
  868.  
  869. pascal OSErr WEReceiveDrag(DragReference drag, WEHandle hWE)
  870. {
  871. #if WASTE_DRAG_AND_DROP
  872.     WEPtr pWE;
  873.     WEActionHandle hAction;
  874.     Handle hText = nil;
  875.     Handle hStyles = nil;
  876.     Handle hFontTable = nil;
  877. #if WASTE_OBJECTS
  878.     Handle hSoup = nil;
  879. #endif
  880.     Handle hObjectData = nil;
  881.     Point mouse;
  882.     LongPt dropLocation;
  883.     SInt32 insertionOffset;
  884.     SInt32 insertionLength;
  885.     SInt32 sourceStart;
  886.     SInt32 sourceEnd;
  887.     SInt32 destStart;
  888.     SInt32 destEnd = 0;
  889.     SInt32 delta = 0;
  890.     UInt16 dragItemIndex;
  891.     UInt16 numDragItems;
  892.     ItemReference dragItem;
  893.     GrafPtr savePort;
  894.     SInt16 intPasteAction;
  895.     WEEdge dropEdge;
  896.     char space = kSpace;
  897.     Boolean isMove = false;
  898.     Boolean isBackwards = false;
  899.     Boolean saveWELock;
  900.     OSErr err;
  901.  
  902.     // lock the WE record
  903.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  904.     pWE = *hWE;
  905.  
  906.     // set up the port
  907.     GetPort(&savePort);
  908.     SetPort(pWE->port);
  909.  
  910.     // stop any ongoing inline input session
  911.     WEStopInlineSession(hWE);
  912.  
  913.     // hide the drag caret
  914.     _WEUpdateDragCaret(kInvalidOffset, hWE);
  915.  
  916.     // refuse this drag if it doesn't taste good
  917.     err = badDragFlavorErr;
  918.     if (!WECanAcceptDrag(drag, hWE))
  919.     {
  920.         goto cleanup;
  921.     }
  922.  
  923.     // get drop location in local coordinates
  924.     if ((err = GetDragMouse(drag, &mouse, nil)) != noErr)
  925.     {
  926.         goto cleanup;
  927.     }
  928.     GlobalToLocal(&mouse);
  929.  
  930.     // for the drag to be accepted, the drop location must be within the view region
  931.     err = dragNotAcceptedErr;
  932.     if (!PtInRgn(mouse, pWE->viewRgn))
  933.     {
  934.         goto cleanup;
  935.     }
  936.  
  937.     // get drop offset into the text
  938.     WEPointToLongPoint(mouse, &dropLocation);
  939.     insertionOffset = WEGetOffset(&dropLocation, &dropEdge, hWE);
  940.     // destStart/destEnd define the range to highlight at the end of the drag
  941.     destStart = insertionOffset;
  942.  
  943.     // drag originated from this same window?
  944.     if (drag == pWE->currentDrag)
  945.     {
  946.  
  947.         // sourceStart/sourceEnd define the range to delete at the end of the move
  948.         sourceStart = pWE->selStart;
  949.         sourceEnd = pWE->selEnd;
  950.  
  951.         // remember text length before insertion
  952.         delta = pWE->textLength;
  953.  
  954.         // if insertion offset is within the original selection range, abort the drag
  955.         // (*err = dragNotAcceptedErr;*)
  956.         if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd))
  957.         {
  958.             goto cleanup;
  959.         }
  960.  
  961.         // if the drag originated from this window, a move,
  962.         // rather than a copy, should be performed
  963.         // Exception: the option key may be held down at mouse-down
  964.         // or mouse-up time to force a copy operation.
  965.  
  966.         isMove = !_WEIsOptionDrag(drag);
  967.         isBackwards = (insertionOffset <= sourceStart);
  968.     } // if intra-window drag
  969.  
  970.     // clear null style
  971.     BCLR(pWE->flags, weFUseNullStyle);
  972.  
  973.     // hide selection highlighting
  974.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  975.  
  976.     // increment modification count
  977.     pWE->modCount++;
  978.  
  979.     // if undo support is enabled, create a new action so we'll be able to undo the insertion
  980.     if (BTST(pWE->features, weFUndoSupport))
  981.     {
  982.         WEClearUndo(hWE);
  983.         if (_WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  984.         {
  985.             _WEPushAction(hAction);
  986.         }
  987.     }
  988.  
  989.     // count items in this drag
  990.     if ((err = CountDragItems(drag, &numDragItems)) != noErr)
  991.     {
  992.         goto cleanup;
  993.     }
  994.  
  995.     for (dragItemIndex = 1; dragItemIndex<=numDragItems; dragItemIndex++)
  996.     {
  997.         // get item reference number for current drag item
  998.         if ((err = GetDragItemReferenceNumber(drag, dragItemIndex, &dragItem)) != noErr)
  999.         {
  1000.             goto cleanup;
  1001.         }
  1002.  
  1003.         // see if this drag item contains a text flavor
  1004.         err = _WEExtractFlavor(drag, dragItem, kTypeText, &hText, insertionOffset, hWE);
  1005.         if (err == noErr)
  1006.         {
  1007.             if (!BTST(pWE->features, weFMonoStyled))
  1008.             {
  1009.                 // extract accompanying styles, if any
  1010.                 err = _WEExtractFlavor(drag, dragItem, kTypeStyles, &hStyles, insertionOffset, hWE);
  1011.                 if ((err != noErr) && (err != badDragFlavorErr))
  1012.                 {
  1013.                     goto cleanup;
  1014.                 }
  1015.                 if (hStyles != nil)
  1016.                 {
  1017.                     //    the styles may be further accompanied by a font table...
  1018.                     err = _WEExtractFlavor(drag, dragItem, kTypeFontTable, &hFontTable,
  1019.                             insertionOffset, hWE);
  1020.                     if ((err != noErr) && (err != badDragFlavorErr))
  1021.                     {
  1022.                         goto cleanup;
  1023.                     }
  1024.                 }
  1025. #if WASTE_OBJECTS
  1026.                 //    extract the object soup, if any
  1027.                 err = _WEExtractFlavor(drag, dragItem, kTypeSoup, &hSoup, insertionOffset, hWE);
  1028.                 if ((err != noErr) && (err != badDragFlavorErr))
  1029.                 {
  1030.                     goto cleanup;
  1031.                 }
  1032. #endif
  1033.             }
  1034.  
  1035.             // any extra space added because of intelligent cut-and-paste rules will use the
  1036.             // style attributes set at the insertion point
  1037.             if (dragItemIndex == 1)
  1038.             {
  1039.                 pWE->selStart = insertionOffset;
  1040.                 pWE->selEnd = insertionOffset;
  1041.                 _WESynchNullStyle(hWE);
  1042.             }
  1043.  
  1044.             // get text length
  1045.             insertionLength = GetHandleSize(hText);
  1046.             destEnd = insertionOffset + insertionLength;
  1047.  
  1048.             // insert the new text at the insertion point
  1049.             HLock(hText);
  1050.             err = _WEInsertText(insertionOffset, *hText, insertionLength, hWE);
  1051.             _WEForgetHandle(&hText);
  1052.             if (err != noErr)
  1053.             {
  1054.                 goto cleanup;
  1055.             }
  1056.  
  1057.             // adjust deletion range length in undo buffer
  1058.             _WEAdjustUndoRange(insertionLength, hWE);
  1059.  
  1060.             if (hStyles != nil)
  1061.             {
  1062.                 if (hFontTable != nil)
  1063.                 {
  1064.                     Boolean wasChanged = false;
  1065.  
  1066.                     // if a font table is available, see whether we should re-map font IDs
  1067.                     if ((WEUpdateFontTable(hFontTable, nil, &wasChanged) == noErr) && wasChanged)
  1068.                     {
  1069.                         WEUpdateStyleScrap((StScrpHandle) hStyles, hFontTable);
  1070.                     }
  1071.                 }
  1072.  
  1073.                 // apply the accompanying styles
  1074.                 if ((err = _WEApplyStyleScrap(insertionOffset, destEnd,
  1075.                             (StScrpHandle) hStyles, hWE)) != noErr)
  1076.                 {
  1077.                     goto cleanup;
  1078.                 }
  1079.                 _WEForgetHandle(&hStyles);
  1080.             }
  1081.  
  1082. #if WASTE_OBJECTS
  1083.             // apply the accompanying soup, if any
  1084.             if (hSoup != nil)
  1085.             {
  1086.                 if ((err = _WEApplySoup(insertionOffset, hSoup, hWE)) != noErr)
  1087.                 {
  1088.                     goto cleanup;
  1089.                 }
  1090.                 _WEForgetHandle(&hSoup);
  1091.             }
  1092. #endif
  1093.  
  1094.             // determine whether an extra space should be added before or after the inserted text
  1095.             intPasteAction = _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  1096.  
  1097.             // add the extra space, if necessary
  1098.             if (intPasteAction != weDontAddSpaces)
  1099.             {
  1100.                 if (intPasteAction == weAddSpaceOnLeftSide)
  1101.                 {
  1102.                     if ((err = _WEInsertText(insertionOffset, &space, sizeof(space), hWE)) != noErr)
  1103.                     {
  1104.                         goto cleanup;
  1105.                     }
  1106.  
  1107.                     destEnd++;
  1108.  
  1109.                     // if an extra space is inserted in front of all dropped items,
  1110.                     // don't count it when eventually highlighting the destination range
  1111.                     if (dragItemIndex == 1)
  1112.                     {
  1113.                         destStart++;
  1114.                     }
  1115.                 }
  1116.                 else
  1117.                 {
  1118.                     if ((err = _WEInsertText(destEnd, &space, sizeof(space), hWE)) != noErr)
  1119.                     {
  1120.                         goto cleanup;
  1121.                     }
  1122.                 }
  1123.  
  1124.                 insertionLength++;
  1125.                 _WEAdjustUndoRange(1, hWE);
  1126.             } // if extra space
  1127.  
  1128.         }
  1129.  
  1130. #if WASTE_OBJECTS
  1131.         else if (err == badDragFlavorErr)
  1132.         {
  1133.             SInt32 objectIndex;
  1134.             FlavorType objectType;
  1135.             Point objectSize;
  1136.             SInt16 saveUndoSupport;
  1137.             SInt16 saveInhibitRecal;
  1138.  
  1139.             objectSize.v = 0;
  1140.             objectSize.h = 0;
  1141.  
  1142.             // no text flavor: there must be a flavor matching one of the registered object types
  1143.             objectIndex = 0;
  1144.             while (_WEGetIndObjectType(objectIndex, &objectType, hWE) == noErr)
  1145.             {
  1146.                 err = _WEExtractFlavor(drag, dragItem, objectType, &hObjectData, insertionOffset, hWE);
  1147.                 if (err == noErr)
  1148.                 {
  1149.                     break;     // enclosing while
  1150.                 }
  1151.                 if (err != badDragFlavorErr)
  1152.                 {
  1153.                     goto cleanup;
  1154.                 }
  1155.                 objectIndex++;
  1156.             } // while
  1157.             if (err != noErr)
  1158.             {
  1159.                 goto cleanup;
  1160.             }
  1161.  
  1162.             // set insertion point on first iteration (*after* extracting flavors, in case we are
  1163.             // doing an intra-window move, otherwise our send proc would be confused)
  1164.             if (dragItemIndex == 1)
  1165.             {
  1166.                 pWE->selStart = insertionOffset;
  1167.                 pWE->selEnd = insertionOffset;
  1168.             }
  1169.  
  1170.             // insert the object, but without touching undo or redrawing the text
  1171.             saveUndoSupport = WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  1172.             saveInhibitRecal = WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  1173.             err = WEInsertObject(objectType, hObjectData, objectSize, hWE);
  1174.             WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  1175.             WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  1176.             if (err != noErr)
  1177.             {
  1178.                 goto cleanup;
  1179.             }
  1180.  
  1181.             insertionLength = 1;
  1182.             destEnd = insertionOffset + 1;
  1183.             pWE->modCount--; // compensate for increment made by WEInsertObject
  1184.             _WEAdjustUndoRange(1, hWE);
  1185.         }
  1186. #endif    // WASTE_OBJECTS
  1187.  
  1188.         else
  1189.             goto cleanup;
  1190.  
  1191.         // advance insertion offset for subsequent drag items, if any
  1192.         insertionOffset += insertionLength;
  1193.  
  1194.     } // for
  1195.  
  1196.     if (isMove)
  1197.     {
  1198.         // adjust source range
  1199.         if (isBackwards)
  1200.         {
  1201.             delta -= pWE->textLength;
  1202.             sourceStart -= delta;
  1203.             sourceEnd -= delta;
  1204.         }
  1205.  
  1206.         // extend range according to intelligent cut-and-paste rules
  1207.         _WEIntelligentCut(&sourceStart, &sourceEnd, hWE);
  1208.  
  1209.         // increment mod count
  1210.         pWE->modCount++;
  1211.  
  1212.         // if undo support is enabled, create a new action so we'll be able to undo the deletion
  1213.         if (BTST(pWE->features, weFUndoSupport))
  1214.         {
  1215.             if (_WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, &hAction) == noErr)
  1216.             {
  1217.                 _WEPushAction(hAction);
  1218.             }
  1219.         }
  1220.  
  1221.         // delete source range
  1222.         delta = pWE->textLength;
  1223.         if ((err = _WEDeleteRange(sourceStart, sourceEnd, hWE)) != noErr)
  1224.         {
  1225.             goto cleanup;
  1226.         }
  1227.  
  1228.         // adjust destination range
  1229.         if (!isBackwards)
  1230.         {
  1231.             delta -= pWE->textLength;
  1232.             destStart -= delta;
  1233.             destEnd -= delta;
  1234.         }
  1235.  
  1236.     } // if isMove
  1237.  
  1238.     // select the range encompassing all items dropped
  1239.     pWE->selStart = destStart;
  1240.     pWE->selEnd = destEnd;
  1241.  
  1242.     // redraw the text
  1243.     if (isMove)
  1244.     {
  1245.         if (sourceStart < destStart)
  1246.         {
  1247.             err = _WERedraw(sourceStart, destEnd, hWE);
  1248.         }
  1249.         else
  1250.         {
  1251.             err = _WERedraw(destStart, sourceEnd, hWE);
  1252.         }
  1253.     }
  1254.     else
  1255.     {
  1256.         err = _WERedraw(destStart, destEnd, hWE);
  1257.     }
  1258.  
  1259. cleanup:
  1260.     // dispose of temporary handles
  1261.     _WEForgetHandle(&hText);
  1262.     _WEForgetHandle(&hStyles);
  1263.     _WEForgetHandle(&hFontTable);
  1264. #if WASTE_OBJECTS
  1265.     _WEForgetHandle(&hSoup);
  1266. #endif
  1267.  
  1268.     // restore the port
  1269.     SetPort(savePort);
  1270.  
  1271.     // unlock the WE record
  1272.     _WESetHandleLock((Handle) hWE, saveWELock);
  1273.  
  1274.     // return result code
  1275.     return err;
  1276. #else
  1277.     #pragma unused(drag, hWE)
  1278.     return dragNotAcceptedErr ;
  1279. #endif    // WASTE_DRAG_AND_DROP
  1280. }
  1281.  
  1282. pascal Boolean WEDraggedToTrash(DragReference drag)
  1283. {
  1284. #if WASTE_DRAG_AND_DROP
  1285.     // return true if the drop location of the specified drag is the trash
  1286.  
  1287.     const int bDirectoryAttr = 4;
  1288.  
  1289.     AEDesc dropLocation, coercedDropLocation;
  1290.     CInfoPBRec pb;
  1291.     FSSpecPtr pSpec;
  1292.     SInt16 trashVRefNum;
  1293.     SInt32 trashDirID;
  1294.     Boolean draggedToTrash;
  1295.  
  1296.     draggedToTrash = false;
  1297.     dropLocation.dataHandle = nil;
  1298.     coercedDropLocation.dataHandle = nil;
  1299.  
  1300.     // get drop location
  1301.     if (GetDropLocation(drag, &dropLocation) != noErr)
  1302.     {
  1303.         goto cleanup;
  1304.     }
  1305.  
  1306.     // do nothing if dropLocation is a null descriptor
  1307.     if (dropLocation.descriptorType == typeNull)
  1308.     {
  1309.         goto cleanup;
  1310.     }
  1311.  
  1312.     // try to coerce the descriptor to a file system specification record
  1313.     if (AECoerceDesc(&dropLocation, typeFSS, &coercedDropLocation) != noErr)
  1314.     {
  1315.         goto cleanup;
  1316.     }
  1317.  
  1318.     // lock the data handle of the coerced descriptor
  1319.     HLock(coercedDropLocation.dataHandle);
  1320.     pSpec = *(FSSpecHandle)coercedDropLocation.dataHandle;
  1321.  
  1322.     // determine the directory ID of the drop location (assuming it's a folder!)
  1323.     BLOCK_CLR(pb);
  1324.     pb.hFileInfo.ioVRefNum = pSpec->vRefNum;
  1325.     pb.hFileInfo.ioDirID = pSpec->parID;
  1326.     pb.hFileInfo.ioNamePtr = pSpec->name;
  1327.     if (PBGetCatInfoSync(&pb) != noErr)
  1328.     {
  1329.         goto cleanup;
  1330.     }
  1331.  
  1332.     // make sure the specified file system object is really a directory
  1333.     if (!BTST(pb.hFileInfo.ioFlAttrib, bDirectoryAttr))
  1334.     {
  1335.         goto cleanup;
  1336.     }
  1337.  
  1338.     // find the directory ID of the trash folder
  1339.     if (FindFolder(pSpec->vRefNum, kTrashFolderType, kDontCreateFolder, &trashVRefNum, &trashDirID) != noErr)
  1340.     {
  1341.         goto cleanup;
  1342.     }
  1343.  
  1344.     // compare the two directory IDs: if they're the same, the drop location is the trash
  1345.     if (pb.dirInfo.ioDrDirID == trashDirID)
  1346.     {
  1347.         draggedToTrash = true;
  1348.     }
  1349.  
  1350. cleanup:
  1351.     // clean up
  1352.     AEDisposeDesc(&dropLocation);
  1353.     AEDisposeDesc(&coercedDropLocation);
  1354.  
  1355.     return draggedToTrash;
  1356. #else
  1357.     #pragma unused(drag)
  1358.     return false ;
  1359. #endif
  1360. }
  1361.  
  1362. #if WASTE_IC_SUPPORT
  1363.  
  1364. pascal void _WEResolveURL(EventModifiers modifiers, SInt32 urlStart, SInt32 urlEnd, WEHandle hWE)
  1365. {
  1366.     WEPtr pWE = *hWE;            // assume WE record is already locked
  1367.     Str255 hint;
  1368.     FourCharCode signature;
  1369.     ProcessSerialNumber psn;
  1370.     ProcessInfoRec info;
  1371.     ICInstance inst;
  1372.     ICError err;
  1373. #if (UNIVERSAL_INTERFACES_VERSION < 0x0300)
  1374.     SInt32 junkLong;
  1375. #else
  1376.     UInt32 junkLong;
  1377. #endif
  1378.     Handle hURL = nil;
  1379.     Boolean saveTextLock;
  1380.  
  1381.     // get the hint string IC will use to parse slack URLs
  1382.     hint[0] = 0;
  1383.     if (pWE->hURLHint != nil)
  1384.     {
  1385.         BlockMoveData(*pWE->hURLHint, hint, StrLength(*pWE->hURLHint) + 1);
  1386.     }
  1387.  
  1388.     // find out the signature of the current process
  1389.     signature = '\?\?\?\?';
  1390.     psn.highLongOfPSN = 0;
  1391.     psn.lowLongOfPSN = 0;
  1392.     if (GetCurrentProcess(&psn) == noErr)
  1393.     {
  1394.         BLOCK_CLR(info);
  1395.         info.processInfoLength = sizeof(info);
  1396.         if (GetProcessInformation(&psn, &info) == noErr)
  1397.         {
  1398.             signature = info.processSignature;
  1399.         }
  1400.     }
  1401.  
  1402.     if (ICStart(&inst, signature) == noErr)
  1403.     {
  1404.         if (ICFindConfigFile(inst, 0, nil) == noErr)
  1405.         {
  1406.             saveTextLock = _WESetHandleLock(pWE->hText, true);
  1407.  
  1408.             // cmd + option click just highlights the URL,
  1409.             // without actually resolving it
  1410.             if (modifiers & optionKey)
  1411.             {
  1412.                 hURL = NewHandle(0);
  1413.                 err = ICParseURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd, hURL);
  1414.                 _WEForgetHandle(&hURL);
  1415.             }
  1416.             else
  1417.             {
  1418.                 err = ICLaunchURL(inst, hint, *pWE->hText, WEGetTextLength(hWE), &urlStart, &urlEnd);
  1419.             }
  1420.             _WESetHandleLock(pWE->hText, saveTextLock);
  1421.             WESetSelection(urlStart, urlEnd, hWE);
  1422.  
  1423.             // flash selection if successful (unless option key was down)
  1424.             if ((err == noErr) && !(modifiers & optionKey))
  1425.             {
  1426.                 Delay(5, &junkLong);
  1427.                 WEDeactivate(hWE);
  1428.                 Delay(5, &junkLong);
  1429.                 WEActivate(hWE);
  1430.             }
  1431.         }
  1432.         ICStop(inst);
  1433.     }
  1434. }
  1435.  
  1436. #endif    // WASTE_IC_SUPPORT
  1437.  
  1438. pascal void WEClick(Point mouseLoc, EventModifiers modifiers, UInt32 clickTime, WEHandle hWE)
  1439. {
  1440.     WEPtr pWE;
  1441.     LongPt thePoint;
  1442.     SInt32 offset, anchor;
  1443.     SInt32 rangeStart, rangeEnd;
  1444.     WEEdge edge;
  1445.     Boolean isMultipleClick;
  1446.     Boolean saveWELock;
  1447. #if WASTE_OBJECTS
  1448.     WEObjectDescHandle hObjectDesc;
  1449. #endif
  1450. #if WASTE_IC_SUPPORT
  1451.     SInt32 urlStart, urlEnd;
  1452. #endif
  1453.  
  1454.     // stop any ongoing inline input session
  1455.     WEStopInlineSession(hWE);
  1456.  
  1457.     // lock the WE record
  1458.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1459.     pWE = *hWE;
  1460.  
  1461. #if WASTE_IC_SUPPORT
  1462.     // remember the selection range before the click
  1463.     urlStart = pWE->selStart;
  1464.     urlEnd = pWE->selEnd;
  1465. #endif
  1466.  
  1467.     // hide the caret if it's showing
  1468.     if (BTST(pWE->flags, weFCaretVisible))
  1469.     {
  1470.         _WEBlinkCaret(hWE);
  1471.     }
  1472.  
  1473.     // find click offset
  1474.     WEPointToLongPoint(mouseLoc, &thePoint);
  1475.     offset = WEGetOffset(&thePoint, &edge, hWE);
  1476.  
  1477.     // determine whether this click is part of a sequence
  1478.     // a single click inside an object selects it, so it's like a double click in a word
  1479.     isMultipleClick = ((clickTime < pWE->clickTime + GetDblTime()) && (offset == pWE->clickLoc));
  1480.  
  1481.     // remember click time, click offset and edge value
  1482.     pWE->clickTime = clickTime;
  1483.     pWE->clickLoc = offset;
  1484.     pWE->clickEdge = edge;
  1485.  
  1486. #if WASTE_OBJECTS
  1487.     // when selected, embedded objects can intercept mouse clicks
  1488.     if (WEGetSelectedObject(&hObjectDesc, hWE) == noErr)
  1489.     {
  1490.         if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1491.         {
  1492.             if (_WEClickObject(mouseLoc, modifiers + isMultipleClick, clickTime, hObjectDesc))
  1493.             {
  1494.                     goto cleanup;
  1495.             }
  1496.         }
  1497.     }
  1498. #endif
  1499.  
  1500.     if ((modifiers & shiftKey) == 0)
  1501.     {
  1502.  
  1503.         // is this click part of a sequence or is it a single click?
  1504.         if (isMultipleClick)
  1505.         {
  1506.             pWE->clickCount++;
  1507.  
  1508.             // a double (triple) click creates an anchor-word (anchor-line)
  1509.             if (pWE->clickCount > 1)
  1510.             {
  1511.                 WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1512.             }
  1513.             else
  1514.             {
  1515.                 WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
  1516.             }
  1517.  
  1518.             offset = pWE->anchorStart;
  1519.         }
  1520.         else
  1521.         {
  1522.             // single-click
  1523.  
  1524. #if WASTE_DRAG_AND_DROP
  1525.             // if the Drag Manager is available and the click went in the selection range,
  1526.             // this click may be the beginning of a drag gesture
  1527.             if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1528.             {
  1529.                 if (_WEOffsetInRange(offset, edge, pWE->selStart, pWE->selEnd))
  1530.                 {
  1531.                     if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) != weNoDragErr)
  1532.                     {
  1533.                         goto cleanup;
  1534.                     }
  1535.                 }
  1536.             }
  1537. #endif
  1538.  
  1539.             pWE->clickCount = 0;
  1540.             anchor = offset;
  1541.         }
  1542.     }
  1543.     else
  1544.     {
  1545.  
  1546.         // if the shift key was down, use the old anchor offset found with the previous click
  1547.         anchor = BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selEnd : pWE->selStart;
  1548.     }
  1549.  
  1550.     // set the weFMouseTracking bit while we track the mouse
  1551.     BSET(pWE->flags, weFMouseTracking);
  1552.  
  1553.     // MOUSE TRACKING LOOP
  1554.     do
  1555.     {
  1556.  
  1557.         // get text offset corresponding to mouse position
  1558.         WEPointToLongPoint(mouseLoc, &thePoint);
  1559.         offset = WEGetOffset(&thePoint, &edge, hWE);
  1560.  
  1561.         // if we're selecting words or lines, pin offset to a word or line boundary
  1562.         if (pWE->clickCount > 0)
  1563.         {
  1564.             if (pWE->clickCount > 1)
  1565.             {
  1566.                 WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
  1567.             }
  1568.             else
  1569.             {
  1570.                 WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
  1571.             }
  1572.  
  1573.             // choose the word/line boundary and the anchor that are farthest away from each other
  1574.             if (offset > pWE->anchorStart)
  1575.             {
  1576.                 anchor = pWE->anchorStart;
  1577.                 offset = rangeEnd;
  1578.             }
  1579.             else
  1580.             {
  1581.                 offset = rangeStart;
  1582.                 anchor = pWE->anchorEnd;
  1583.             }
  1584.         }
  1585.         else
  1586.         {
  1587.             // if the point is in the middle of an object, the selection should include it
  1588.             if (edge == kObjectEdge)
  1589.             {
  1590.                 offset++;
  1591.             }
  1592.         }
  1593.  
  1594.         // set the selection range from anchor point to current offset
  1595.         WESetSelection(anchor, offset, hWE);
  1596.  
  1597.         // call the click loop callback, if any
  1598.         if (pWE->clickLoop != nil)
  1599.         {
  1600.             if (!CallWEClickLoopProc(hWE, pWE->clickLoop))
  1601.             {
  1602.                 break;
  1603.             }
  1604.         }
  1605.  
  1606.         // update mouse position
  1607.         GetMouse(&mouseLoc);
  1608.  
  1609.     } while(WaitMouseUp());
  1610.  
  1611.     // clear the weFMouseTracking bit
  1612.     BCLR(pWE->flags, weFMouseTracking);
  1613.  
  1614.     // redraw the caret immediately if the selection range is empty
  1615.     if (anchor == offset)
  1616.     {
  1617.         _WEBlinkCaret(hWE);
  1618.     }
  1619.  
  1620. #if WASTE_IC_SUPPORT
  1621.     if (modifiers & cmdKey)
  1622.     {
  1623.         // command+clicking a URL tries to resolve it
  1624.         // we normally ask IC to parse the text surrounding the clicked point,
  1625.         // but if a selection already existed prior to the click, we pass
  1626.         // that to IC rather than forcing a re-parse
  1627.         if ((anchor != offset) || (anchor < urlStart) || (anchor > urlEnd))
  1628.         {
  1629.             urlStart = anchor;
  1630.             urlEnd = offset;
  1631.         }
  1632.         _WEResolveURL(modifiers, urlStart, urlEnd, hWE);
  1633.     }
  1634. #endif
  1635.  
  1636. cleanup:
  1637.     // unlock the WE record
  1638.     _WESetHandleLock((Handle) hWE, saveWELock);
  1639.  
  1640. }
  1641.  
  1642. pascal UInt16 WEGetClickCount(WEHandle hWE)
  1643. {
  1644.     return (*hWE)->clickCount;
  1645. }
  1646.